#include <iostream>
#include <fstream>
#include <iomanip>

#include "main.h"
#include "ar_usb.h"
#include "ar8030_usb_tool.h"

#if (__CURRENT_PLATFORM__ == __LINUX_PLATFORM__)
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <unistd.h>
#endif
using namespace std;

#define ERR_EXIT(errcode) do { printf("   %s\n", libusb_strerror((enum libusb_error)errcode)); return -1; } while (0)
#define CALL_CHECK_CLOSE(fcall, hdl) do { int _r=fcall; if (_r < 0) { libusb_close(hdl); ERR_EXIT(_r); } } while (0)

#pragma pack(push)
#pragma pack(1)
struct artousb_cmd_header
{
    unsigned char   magic_head[2];
    unsigned short  len;
    unsigned long long dst_addr;//ar9301 is 64bit soc
};
#pragma pack(pop)

unsigned char magic_header[8] = { 0xff, 0x55, 0xff, 0xaa, 0x41, 0x52, 0x54, 0x4f };

ar_usb::ar_usb()
{
    int ret;

    devs        = NULL;
    dev_cnt     = -1;

    ret = libusb_init(NULL);
    if (ret < 0)
    {
        cout << "libusb init failed!" << endl;
    }

    dev_cnt = libusb_get_device_list(NULL, &devs);
    if (dev_cnt < 0)
    {
        cout << "libusb get device failed!" << endl;
    }

    memset(&fragment_info, 0, sizeof(STRU_USB_FRAGMENT_INFO));
}

ar_usb::~ar_usb()
{
}


int ar_usb::print_one_dev_list(libusb_device* dev, int index)
{
    struct libusb_device_descriptor desc;
    struct libusb_config_descriptor* config;
    //struct libusb_interface_descriptor interface;
    libusb_device_handle* handle = NULL;
    char description[512];
    char string[256];
    char interface_name[256] = "";
    unsigned char path[8];
    int ret;

    ret = libusb_get_device_descriptor(dev, &desc);
    if (ret < 0) {
        cout << "failed to get device descriptor" << endl;
        return -1;
    }

    if (ARTO_ROMCODE_VID != desc.idVendor && 
        ARTO_RTOS_VID != desc.idVendor)
    {
        return -1;
    }

    ret = libusb_open(dev, &handle);
    if (LIBUSB_SUCCESS == ret) {
        if (desc.iManufacturer) {
            ret = libusb_get_string_descriptor_ascii(handle,
                                                     desc.iManufacturer,
                                                     (unsigned char*)string,
                                                     sizeof(string));
            if (ret > 0) {
                snprintf(description, sizeof(description), "%s - ", (char*)string);
            } else {
                snprintf(description, sizeof(description), "%04X - ", desc.idVendor);
            }
        } else
            snprintf(description, sizeof(description), "%04X - ",
                desc.idVendor);

        if (desc.iProduct)
        {
            ret = libusb_get_string_descriptor_ascii(handle, desc.iProduct, (unsigned char*)string, sizeof(string));
            if (ret > 0)
                snprintf(description + strlen(description), sizeof(description) -
                    strlen(description), "%s", string);
            else
                snprintf(description + strlen(description), sizeof(description) -
                    strlen(description), "%04X", desc.idProduct);
        }
        else
            snprintf(description + strlen(description), sizeof(description) -
                strlen(description), "%04X", desc.idProduct);
    } else {
        snprintf(description, sizeof(description), "%04X - %04X",
            desc.idVendor, desc.idProduct);
    }

    ret = libusb_get_config_descriptor(dev, 0, &config);
    if (ret < 0)
    {
        cout << "failed to get configure descriptor" << endl;
        return -1;
    }

    if (config->interface[0].altsetting[0].iInterface)
    {
        ret = libusb_get_string_descriptor_ascii(handle,
            config->interface[0].altsetting[0].iInterface,
            (unsigned char*)interface_name, sizeof(interface_name));
        if (ret < 0)
            return -1;
    }

    //romcode may has different pid
    if ((ARTO_ROMCODE_VID == desc.idVendor && ARTO_ROMCODE_PID == desc.idProduct) || 0 == strncmp(ARTO_ROMCODE_NAME, interface_name, strlen(ARTO_ROMCODE_NAME)))
    {
        snprintf(string, sizeof(string), "ROMCODE MODE");
    }
    /*else if ((ARTO_RTOS_VID == desc.idVendor && ARTO_RTOS_PID == desc.idProduct) && 0 == strncmp(ARTO_RTOS_NAME, interface_name, strlen(ARTO_RTOS_NAME)))
    {
        snprintf(string, sizeof(string), "UPGRADE MODE");
    }*/
    else
    {
        snprintf(string, sizeof(string), "NORMAL MODE");
    }

    cout << "==============>Device :" << index << ", Mode :" << string << ", Interface :" 
        << interface_name << ", speed:" << libusb_get_device_speed(dev) << endl;
    cout << "Dev (bus " << dec << (int)libusb_get_bus_number(dev) << ", device "
        << dec << (int)libusb_get_device_address(dev) << "): " << description << endl;

    int busnum = libusb_get_bus_number(dev);

    ret = libusb_get_port_numbers(dev, path, sizeof(path));
    if (ret > 0) 
    {
        printf(" path: %d-%d", busnum, path[0]);
        for (int j = 1; j < ret; j++)
            printf(".%d", path[j]);
    }
    printf("\n");

    if (handle)
        libusb_close(handle);

    return 0;
}

int ar_usb::usb_print_dev_list()
{
    int i = 0, ret = -1;

    for (i = 0; i < dev_cnt; ++i)
    {
        ret = print_one_dev_list(devs[i], i);
        if (-1 == ret)
        {
            //cout << "fail to get device" << i << endl;
        }
    }

    return 0;
}

libusb_device_handle * ar_usb::usb_open_by_index(unsigned int index)
{
    int ret;
    libusb_device_handle * handle = NULL;

    ret = libusb_open(devs[index], &handle);
    if (LIBUSB_SUCCESS != ret)
    {
        return NULL;
    }

    //libusb_set_auto_detach_kernel_driver (handle, 1);
    if (libusb_kernel_driver_active(handle, 0) == 1)
    {
        printf("USB Kernel Driver Active \n");
        if (libusb_detach_kernel_driver(handle, 0) == 0)
        {
            printf("Kernel Driver Detached \n");
        }
    }

    ret = libusb_claim_interface(handle, 0);
    if (ret < 0)
    {
        cout << "claim interface error " << ret << "-" << libusb_strerror(ret) << endl;
        return NULL;
    }

    /*libusb_device* dev = NULL;
    struct libusb_config_descriptor* conf_desc;
    int                             nb_ifaces;
    int                             iface, first_iface = -1;
    unsigned char* ret_ptr;

    if (NULL == handle)
        return NULL;

    dev = devs[index];

    if (libusb_get_config_descriptor(dev, 0, &conf_desc) < 0)
        return NULL;

    nb_ifaces = conf_desc->bNumInterfaces;
    if (nb_ifaces > 0)
    {
        first_iface = conf_desc->interface[0].altsetting[0].bInterfaceNumber;
    }

    if ((iface = usb_find_class_interface(conf_desc, nb_ifaces,
        LIBUSB_CLASS_HID,
        &hid_endpoint_in,
        &hid_endpoint_out,
        &hid_endpoint_in_max_size,
        &hid_endpoint_out_max_size)) < 0)
    {
        cout << "find hid class failed!" << endl;
        return NULL;
    }

    libusb_free_config_descriptor(conf_desc);

    libusb_set_auto_detach_kernel_driver(handle, 1);
    //cout << "\nClaiming interface " << iface << "...\n" << endl;
    ret = libusb_claim_interface(handle, iface);
    if (ret != LIBUSB_SUCCESS)
    {
        cout << "   Failed.(" << ret << ")\n" << endl;
    }

    printf("claimed interface\n");*/

    return handle;
}

int ar_usb::usb_class_configure(libusb_device_handle* handle, unsigned int index)
{
    int ret;
    libusb_device* dev = NULL;
    struct libusb_config_descriptor* conf_desc;
    int                             nb_ifaces;
    int                             iface, first_iface = -1;
    //unsigned char* ret_ptr;

    if (NULL == handle)
        return -1;

    dev = devs[index];

    if (libusb_get_config_descriptor(dev, 0, &conf_desc) < 0)
        return -1;

    nb_ifaces = conf_desc->bNumInterfaces;
    if (nb_ifaces > 0)
    {
        first_iface = conf_desc->interface[0].altsetting[0].bInterfaceNumber;
    }

    if ((iface = usb_find_class_interface(conf_desc, nb_ifaces,
        LIBUSB_CLASS_HID,
        &hid_endpoint_in,
        &hid_endpoint_out,
        &hid_endpoint_in_max_size,
        &hid_endpoint_out_max_size)) < 0)
    {
        cout << "find hid class failed!" << endl;
        return -1;
    }

    libusb_free_config_descriptor(conf_desc);

    //libusb_set_auto_detach_kernel_driver (handle, 1);
    if (libusb_kernel_driver_active(handle, 0) == 1)
    {
        printf("USB Kernel Driver Active \n");
        if (libusb_detach_kernel_driver(handle, 0) == 0)
        {
            printf("Kernel Driver Detached \n");
        }
    }
    //cout << "\nClaiming interface " << iface << "...\n" << endl;
    ret = libusb_claim_interface(handle, iface);
    if (ret != LIBUSB_SUCCESS)
    {
        cout << "   Failed.(" << ret << ")\n" << endl;
    }

    printf("claimed interface\n");

    return ret;
}

int ar_usb::usb_close_dev(libusb_device_handle * handle)
{
    if (handle == NULL)
    {
        cout << "handle is NULL" << endl;
        return -1;
    }

    libusb_close(handle);
    return 0;
}

int ar_usb::usb_reset_dev(libusb_device_handle * handle)
{
    if (handle == NULL)
    {
        cout << "handle is NULL" << endl;
        return -1;
    }

    return libusb_reset_device(handle);
}

int ar_usb::usb_transfer_data_with_addr_romcode(libusb_device_handle * handle, unsigned long long addr, unsigned char* data, unsigned int len, unsigned int timeout)
{
    int i, ret, tmp = 0;
    unsigned char tmp_buffer[ARTO_USB_EP0_TRAN_SIZE];
    struct artousb_cmd_header* cmd = (struct artousb_cmd_header*)tmp_buffer;
    unsigned char* data_buf = tmp_buffer + sizeof(struct artousb_cmd_header);

    if (handle == NULL)
    {
        cout << "handle is NULL" << endl;
        return -1;
    }

    cmd->magic_head[0] = ARTO_USB_MAGIC_NUM_0;
    cmd->magic_head[1] = ARTO_USB_MAGIC_NUM_1;

    for (i = 0; i < len / ARTO_USB_EP0_MAX_PAYLOAD_SIZE; ++i)
    {
        cmd->len = ARTO_USB_EP0_MAX_PAYLOAD_SIZE;
        cmd->dst_addr = addr + i * ARTO_USB_EP0_MAX_PAYLOAD_SIZE;
        memcpy(data_buf, data + i * ARTO_USB_EP0_MAX_PAYLOAD_SIZE, cmd->len);
        //printf("0x%08llx 0x%08x %d\n", cmd->dst_addr, *(unsigned int*)data, cmd->len);
        ret = libusb_control_transfer(handle, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
            0x0c, 0, 0x0, tmp_buffer, ARTO_USB_EP0_TRAN_SIZE, timeout);
        if (ret < 0)
        {
            cout << "libusb_control_transfer transfer failed! ret:" << ret << "timeout:" << timeout << endl;
        }
    }

    cmd->len = (unsigned short)(len - i * ARTO_USB_EP0_MAX_PAYLOAD_SIZE);
    if (cmd->len || 0 == i)
    {
        cmd->dst_addr = addr + i * ARTO_USB_EP0_MAX_PAYLOAD_SIZE;
        memcpy(data_buf, data + i * ARTO_USB_EP0_MAX_PAYLOAD_SIZE, cmd->len);

        //if (4 == cmd->len && 0 == (cmd->dst_addr & 0x3))
        //    printf("0x%08llx 0x%08x %d\n", cmd->dst_addr, *(unsigned int*)data, cmd->len);

        ret = libusb_control_transfer(handle, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
            0x0c, 0, 0x0, tmp_buffer, cmd->len + sizeof(struct artousb_cmd_header), timeout);
        if (ret < 0)
        {
            cout << "libusb_control_transfer transfer failed!" << endl;
        }
    }

    return ret;
}

int ar_usb::usb_exit_romcode(libusb_device_handle * handle, unsigned int timeout)
{
    int ret, tmp = 0;
    unsigned char tmp_buffer[ARTO_USB_EP0_TRAN_SIZE];
    struct artousb_cmd_header* cmd = (struct artousb_cmd_header*)tmp_buffer;
    unsigned char* data_buf = tmp_buffer + sizeof(struct artousb_cmd_header);

    cmd->magic_head[0] = ARTO_USB_MAGIC_NUM_0;
    cmd->magic_head[1] = ARTO_USB_MAGIC_NUM_1;

    //send exit artousb
    cmd->len = ARTO_USB_EXIT_LEN;
    cmd->dst_addr = ARTO_USB_EXIT_ADDR;
    ret = libusb_control_transfer(handle, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
        0x0c, 0, 0x0, tmp_buffer, cmd->len + sizeof(struct artousb_cmd_header), timeout);
    if (ret < 0)
    {
        cout << "usb_exit_romcode failed!" << endl;
    }

    return ret;
}

int ar_usb::usb_find_class_interface(struct libusb_config_descriptor* conf_desc, int nb_ifaces,
    libusb_class_code class_id,
    uint8_t* endpoint_in,
    uint8_t* endpoint_out,
    uint32_t* endpoint_in_size,
    uint32_t* endpoint_out_size)
{
    struct libusb_endpoint_descriptor* endpoint;
    int                                    i, j, k;

    for (i = 0; i < nb_ifaces; i++)
    {
        //printf("              interface[%d]: id = %d\n", i,
        //    conf_desc->interface[i].altsetting[0].bInterfaceNumber);

        if (class_id != conf_desc->interface[i].altsetting[0].bInterfaceClass)
        {
            continue;
        }

        for (j = 0; j < conf_desc->interface[i].num_altsetting; j++)
        {
            /*printf("interface[%d].altsetting[%d]: num endpoints = %d\n",
                i, j, conf_desc->interface[i].altsetting[j].bNumEndpoints);

            printf("   Class.SubClass.Protocol: %02X.%02X.%02X\n",
                conf_desc->interface[i].altsetting[j].bInterfaceClass,
                conf_desc->interface[i].altsetting[j].bInterfaceSubClass,
                conf_desc->interface[i].altsetting[j].bInterfaceProtocol);*/

            for (k = 0; k < conf_desc->interface[i].altsetting[j].bNumEndpoints; k++)
            {
                struct libusb_ss_endpoint_companion_descriptor* ep_comp = NULL;
                endpoint = (struct libusb_endpoint_descriptor*) & conf_desc->interface[i].altsetting[j].endpoint[k];
                /*printf("       endpoint[%d].address: %02X\n", k, endpoint->bEndpointAddress);

                printf("           max packet size: %04X\n", endpoint->wMaxPacketSize);
                printf("          polling interval: %02X\n", endpoint->bInterval);*/

                libusb_get_ss_endpoint_companion_descriptor(NULL, endpoint, &ep_comp);
                if (ep_comp)
                {
                    /*printf("                 max burst: %02X   (USB 3.0)\n", ep_comp->bMaxBurst);
                    printf("        bytes per interval: %04X (USB 3.0)\n", ep_comp->wBytesPerInterval);*/
                    libusb_free_ss_endpoint_companion_descriptor(ep_comp);
                }

                // Use the first interrupt or bulk IN/OUT endpoints as default for testing
                if ((endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) & (LIBUSB_TRANSFER_TYPE_BULK | LIBUSB_TRANSFER_TYPE_INTERRUPT))
                {
                    if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN)
                    {
                        *endpoint_in = endpoint->bEndpointAddress;
                        *endpoint_in_size = endpoint->wMaxPacketSize;
                    }
                    else
                    {
                        *endpoint_out = endpoint->bEndpointAddress;
                        *endpoint_out_size = endpoint->wMaxPacketSize;
                    }
                }
            }

            if (*endpoint_in && *endpoint_out)
                return i;
        }
    }
    return -1;
}

unsigned int ar_usb::usb_get_checksum(unsigned char* data, unsigned int len)
{
    unsigned int checksum = 0;

    while (len)
    {
        checksum += *data++;
        --len;
    }

    return checksum;
}

int ar_usb::usb_magic_num_check(unsigned char* data)
{
    unsigned int i;
    for (i = 0; i < sizeof(magic_header); ++i)
    {
        if (data[i] != magic_header[i])
            return -1;
    }
    return 0;
}
//static unsigned char g_tmp_buffer[100 * 1024 * 1024];
//static unsigned int g_tmp_buffer_len = 0;

void hid_callback(libusb_transfer* hid_transfer)
{
    libusb_free_transfer(hid_transfer);
}

int ar_usb::usb_hid_write(libusb_device_handle* handle, unsigned char* data, unsigned int buf_len, unsigned int timeout)
{
    int ret = -1, try_time = 10;

    for (; try_time > 0 && LIBUSB_SUCCESS != ret; --try_time)
    {
        //printf("hid_endpoint_out %d\n", hid_endpoint_out);
        ret = libusb_bulk_transfer(handle, hid_endpoint_out, data, buf_len, NULL, timeout);
        if (LIBUSB_SUCCESS != ret)
        {
#if (__CURRENT_PLATFORM__ == __LINUX_PLATFORM__)
            usleep(125);
#else
            Sleep(1);
#endif
            printf("libusb_interrupt_transfer failed! %d: %s\n", ret, libusb_strerror((enum libusb_error)ret));
        }

        if (LIBUSB_SUCCESS == ret && 0 == (buf_len % 512))// end transfer
        {
            ret = libusb_bulk_transfer(handle, hid_endpoint_out, data, 0, NULL, timeout);
            if (LIBUSB_SUCCESS != ret)
            {
#if (__CURRENT_PLATFORM__ == __LINUX_PLATFORM__)
                usleep(125);
#else
                Sleep(1);
#endif
                printf("libusb_interrupt_transfer failed! %d: %s\n", ret, libusb_strerror((enum libusb_error)ret));
            }
        }
    }

    return ret;
}

int ar_usb::usb_hid_read(libusb_device_handle* handle, unsigned char* data, unsigned int buf_len, unsigned int timeout)
{
    int ret = -1, try_time = 10;
    int real_len = 0;

    for (; try_time > 0 && LIBUSB_SUCCESS != ret; --try_time)
    {
        //printf("hid_endpoint_in %d\n", hid_endpoint_in);
        ret = libusb_bulk_transfer(handle, hid_endpoint_in, data, buf_len, &real_len, timeout);
        if (LIBUSB_SUCCESS != ret)
        {
#if (__CURRENT_PLATFORM__ == __LINUX_PLATFORM__)
            usleep(125);
#else
            Sleep(1);
#endif
            printf("libusb_interrupt_transfer failed! %d: %s\n", ret, libusb_strerror((enum libusb_error)ret));
        }
    }

    if(LIBUSB_SUCCESS == ret)
        ret = real_len;

    return ret;
}

#if 0
int ar_usb::usb_hid_send_data(libusb_device_handle* handle, unsigned char* data, unsigned int buf_len, unsigned int timeout)
{
    int ret, index;
    unsigned int percent = 0;

    if (NULL == data)
    {
        cout << "data is NULL" << endl;
        return -1;
    }

    STRU_HID_HEADER* phid = (STRU_HID_HEADER*)((char*)data - sizeof(STRU_HID_HEADER));
    STRU_USB_HEADER* pusb = (STRU_USB_HEADER*)((char*)phid - sizeof(STRU_USB_HEADER));

    unsigned int offset = 0;
    unsigned int step = hid_endpoint_out_max_size - sizeof(STRU_USB_HEADER);
    unsigned int fragment_num = ((phid->header_len + phid->data_len) % step) ?
        ((phid->header_len + phid->data_len) / step) + 1 :
        ((phid->header_len + phid->data_len) / step);

    if (buf_len == phid->data_len)
    {
        if (NULL != handle)
        {
            //write_mutex.lock();
            //pthread_mutex_lock(&(hid_device.wrt_hid_mutex));
            for (offset = 0, index = 0; (offset + step) < (phid->header_len + phid->data_len); offset += step, pusb = (STRU_USB_HEADER*)((unsigned char*)pusb + step), ++index)
            {
                memcpy(pusb->magic_num, magic_header, 8);
                pusb->header_len = 36;
                pusb->total_len = phid->header_len + phid->data_len;
                pusb->payload_len = step;
                pusb->fragment_idx = index;
                pusb->fragment_num = fragment_num;
                pusb->sub_proto = HID_PROTOCOL;
                pusb->reserved[0] = 0;
                pusb->reserved[1] = 0;
                pusb->reserved[2] = 0;
                pusb->checksum = usb_get_checksum(((unsigned char*)pusb + sizeof(STRU_USB_HEADER)),
                    pusb->payload_len);

                ret = usb_hid_write(handle, (unsigned char*)pusb, hid_endpoint_out_max_size, timeout);
                if (ret < 0)
                {
                    cout << "write hid failed!(" << ret << ":"<< strerror(errno) << ")" << endl;
                    free(data - sizeof(STRU_HID_HEADER) - sizeof(STRU_USB_HEADER));
                    //write_mutex.unlock();
                    //pthread_mutex_unlock(&(hid_device.wrt_hid_mutex));
                    return -1;
                }

                if (percent / 2 != ((unsigned long long)offset) * 100U / pusb->total_len / 2)
                {
                    percent = ((unsigned long long)offset) * 100U / pusb->total_len;
                    std::cout << "\rusb send " << setw(3) << percent << "%";
                    string progress = "[" + string(percent / 2, '*') + string(50 - percent / 2, ' ') + "]";
                    cout << "" << progress << flush;
                }
                //printf("i:%d offset:%d total:%d\n", pusb->fragment_idx, offset, pusb->total_len);
            }

            memcpy(pusb->magic_num, magic_header, 8);
            pusb->header_len = 36;
            pusb->total_len = phid->header_len + phid->data_len;
            pusb->payload_len = pusb->total_len - offset;
            pusb->fragment_idx = index;
            pusb->fragment_num = fragment_num;
            pusb->sub_proto = HID_PROTOCOL;
            pusb->reserved[0] = 0;
            pusb->reserved[1] = 0;
            pusb->reserved[2] = 0;
            pusb->checksum = usb_get_checksum(((unsigned char*)pusb + sizeof(STRU_USB_HEADER)),
                pusb->payload_len);

            ret = usb_hid_write(handle, (unsigned char*)pusb, pusb->payload_len + pusb->header_len, timeout);
            if (ret < 0)
            {
                cout << "write hid failed!(" << ret << ")" << endl;
                free(data - sizeof(STRU_HID_HEADER) - sizeof(STRU_USB_HEADER));
                //write_mutex.unlock();
                //pthread_mutex_unlock(&(hid_device.wrt_hid_mutex));
                return -1;
            }

            if (percent != (((unsigned long long)offset) + pusb->payload_len) * 100U / pusb->total_len)
            {
                percent = (((unsigned long long)offset) + pusb->payload_len) * 100U / pusb->total_len;
                std::cout << "\rusb send " << setw(3) << percent << "%";
                string progress = "[" + string(percent / 2, '*') + string(50 - percent / 2, ' ') + "]";
                cout << "" << progress << endl << flush;
                //emit set_progress(__FUNCTION__, percent);
            }

            cout << "end" << endl;
            //printf("i:%d offset:%d total:%d\n", pusb->fragment_idx, offset, pusb->total_len);
            //write_mutex.unlock();
            //pthread_mutex_unlock(&(hid_device.wrt_hid_mutex));
        }
        else
        {
            ret = -1;
        }

        free(data - sizeof(STRU_HID_HEADER) - sizeof(STRU_USB_HEADER));
    }
    else
    {
        cout << "memory buffer len no match(" << buf_len << " " << phid->data_len << "), can not free memory!" << endl;
        ret = -1;
    }

    return ret;
}

unsigned char* ar_usb::usb_hid_recv_data(libusb_device_handle* handle,
                                        unsigned int* recv_len, unsigned int timeout)
{
    int                 ret             = -1;
    int                 tmp_recv_len    = 0;
    STRU_USB_HEADER     * pusb_header   = NULL;
    unsigned char       * recv_data     = NULL;
    unsigned char       recv_buffer[4096];

    if (NULL == handle)
    {
        cout << "open usb first!" << endl;
        return NULL;
    }

    while (1)
    {
        ret = libusb_interrupt_transfer(handle, hid_endpoint_in, recv_buffer, hid_endpoint_in_max_size, &tmp_recv_len, timeout);
        if (LIBUSB_SUCCESS != ret)
        {
            //emit print_log("hid_recv_data failed\n");
            return NULL;
        }

        pusb_header = (STRU_USB_HEADER*)recv_buffer;
        if (0 == usb_magic_num_check(pusb_header->magic_num))//header check
        {
            if (HID_PROTOCOL == pusb_header->sub_proto)//usb protocol check
            {
                if (pusb_header->payload_len <= hid_endpoint_in_max_size)//mtu check
                {
                    if (pusb_header->fragment_num > 1)//more fragment
                    {
                        if (pusb_header->fragment_idx == 0)//first fragment
                        {
                            recv_data = (unsigned char*)calloc(pusb_header->total_len + pusb_header->header_len + 1, sizeof(unsigned char));// add 1 for '\0'
                            if (NULL == recv_data)
                            {
                                ret = -1;
                                printf("recv_data calloc failed!\n");
                            }
                            else
                            {
                                fragment_info.fragment_idx = 0;
                                *recv_len = 0;
                            }
                        }
                        else if (fragment_info.fragment_idx != pusb_header->fragment_idx)//check fragment index
                        {
                            printf("index no match!\n");
                            if (fragment_info.fragment_idx > pusb_header->fragment_idx)
                            {
                                printf("find repeat data!\n");
                                continue;
                            }
                        }

                        if (NULL == recv_data)
                        {
                            printf("recv_data is NULL!\n");
                        }
                        else
                        {
                            STRU_HID_HEADER* phid_header;
                            phid_header = (STRU_HID_HEADER*)recv_data;

                            memcpy(recv_data + *recv_len, recv_buffer + sizeof(STRU_USB_HEADER), tmp_recv_len - sizeof(STRU_USB_HEADER));
                            /*if (INDEX_SYS_FILEGET == phid_header->port_index)
                            {
                                set_progress(__FUNCTION__, ((unsigned long long)(*recv_len)) * 100 / (phid_header->data_len + phid_header->header_len));
                            }*/
                        }

                        *recv_len += (tmp_recv_len - sizeof(STRU_USB_HEADER));
                        ++fragment_info.fragment_idx;//next fragment index

                        if (pusb_header->fragment_idx == pusb_header->fragment_num - 1)
                        {
                            memset(&fragment_info, 0, sizeof(STRU_USB_FRAGMENT_INFO));
                            break;
                        }
                    }
                    else//one fragment
                    {
                        *recv_len = tmp_recv_len - sizeof(STRU_USB_HEADER);
                        recv_data = (unsigned char*)calloc((*recv_len) + 1, sizeof(unsigned char));// add 1 for '\0'
                        if (NULL == recv_data)
                        {
                            ret = -1;
                            printf("recv_data calloc failed!\n");
                        }
                        else
                        {
                            memcpy(recv_data, recv_buffer + sizeof(STRU_USB_HEADER), *recv_len * sizeof(unsigned char));
                        }
                        break;
                    }
                }
                else
                {
                    ret = -1;
                    cout << "size out of mtu " << hid_endpoint_in_max_size << " " << pusb_header->payload_len + "!\n";
                    break;
                }
            }
            else
            {
                ret = -1;
                cout << "sub_proto no match hid (" << HID_PROTOCOL << ":" << pusb_header->sub_proto << ")!\n";
                break;
            }
        }
        else
        {
            ret = -1;
            printf("magic num error!\n");
            break;
        }
    }

    if (-1 == ret)
    {
        if (NULL != recv_data)
        {
            free(recv_data);
            recv_data = NULL;
        }
        return NULL;
    }
    return recv_data;
}

unsigned char* ar_usb::usb_hid_get_send_buffer(unsigned int buf_len, unsigned int port)
{
    unsigned char* buff = NULL;
    STRU_HID_HEADER* phead;

    buff = (unsigned char*)malloc(buf_len + sizeof(STRU_HID_HEADER) + sizeof(STRU_USB_HEADER));
    if (NULL == buff)
    {
        cout << "malloc buffer failed!" << endl;
        return NULL;
    }
    memset(buff, 0, buf_len + sizeof(STRU_HID_HEADER) + sizeof(STRU_USB_HEADER));

    phead = (STRU_HID_HEADER*)(buff + sizeof(STRU_USB_HEADER));
    phead->data_len = buf_len;
    phead->header_len = sizeof(STRU_HID_HEADER);
    phead->port_index = port;
    phead->status = 0;
    phead->reserved[0] = 0;
    phead->reserved[1] = 0;
    phead->reserved[2] = 0;

    return buff + sizeof(STRU_HID_HEADER) + sizeof(STRU_USB_HEADER);
}

int ar_usb::usb_transfer_data_uboot(libusb_device_handle* handle, unsigned int index, 
                                    unsigned char* data, unsigned int len, unsigned int timeout)
{
    libusb_device                   * dev = NULL;
    struct libusb_config_descriptor * conf_desc;
    int                             nb_ifaces;
    int                             iface, first_iface = -1;
    int                             ret;

    if (NULL == handle)
        return -1;

    dev = devs[index];

    CALL_CHECK_CLOSE(libusb_get_config_descriptor(dev, 0, &conf_desc), handle);
    nb_ifaces = conf_desc->bNumInterfaces;
    if (nb_ifaces > 0)
    {
        first_iface = conf_desc->interface[0].altsetting[0].bInterfaceNumber;
    }

    if ((iface = usb_find_class_interface(conf_desc, nb_ifaces,
        LIBUSB_CLASS_HID,
        &hid_endpoint_in,
        &hid_endpoint_out,
        &hid_endpoint_in_max_size,
        &hid_endpoint_out_max_size)) < 0)
    {
        cout << "find hid class failed!" << endl;
        return -1;
    }

    libusb_free_config_descriptor(conf_desc);

    libusb_set_auto_detach_kernel_driver(handle, 1);
    //cout << "\nClaiming interface " << iface << "...\n" << endl;
    ret = libusb_claim_interface(handle, iface);
    if (ret != LIBUSB_SUCCESS)
    {
        cout << "   Failed.(" << ret << ")\n" << endl;
    }

    if (NULL == data)
    {
        cout << "data is NULL" << endl;
        return -1;
    }

    ret = usb_hid_send_data(handle, data, len, timeout);

    //printf("\n");
    //printf("Releasing interface %d...\n", iface);
    libusb_release_interface(handle, iface);

    return ret;
}

unsigned char * ar_usb::usb_recv_data_uboot(libusb_device_handle* handle, unsigned int index,
                                unsigned int * recv_len, unsigned int timeout)
{
    libusb_device                   * dev = NULL;
    struct libusb_config_descriptor * conf_desc;
    int                             nb_ifaces;
    int                             iface, first_iface = -1;
    int                             ret = -1;
    unsigned char                   * ret_ptr;

    if (NULL == handle)
        return NULL;

    dev = devs[index];

    if (libusb_get_config_descriptor(dev, 0, &conf_desc) < 0)
        return NULL;

    nb_ifaces = conf_desc->bNumInterfaces;
    if (nb_ifaces > 0)
    {
        first_iface = conf_desc->interface[0].altsetting[0].bInterfaceNumber;
    }

    if ((iface = usb_find_class_interface(conf_desc, nb_ifaces,
        LIBUSB_CLASS_HID,
        &hid_endpoint_in,
        &hid_endpoint_out,
        &hid_endpoint_in_max_size,
        &hid_endpoint_out_max_size)) < 0)
    {
        cout << "find hid class failed!" << endl;
        return NULL;
    }

    libusb_free_config_descriptor(conf_desc);

    libusb_set_auto_detach_kernel_driver(handle, 1);
    //cout << "\nClaiming interface " << iface << "...\n" << endl;
    ret = libusb_claim_interface(handle, iface);
    if (ret != LIBUSB_SUCCESS)
    {
        cout << "   Failed.(" << ret << ")\n" << endl;
    }

    ret_ptr = usb_hid_recv_data(handle, recv_len, timeout);

    //printf("\n");
    //printf("Releasing interface %d...\n", iface);
    libusb_release_interface(handle, iface);

    return ret_ptr;
}

#endif